
#ifndef PRIVATE_KEY_HPP
#define PRIVATE_KEY_HPP

#include "PublicKey.hpp"
#include "wire/hash.hpp"
#include "wire/serialize.hpp"
#include "wire/uint256.hpp"
#include "secp256k1/include/secp256k1.h"
#include "secp256k1/include/secp256k1_recovery.h"
#include <openssl/rand.h>
#include <stdexcept>
#include <vector>

typedef std::vector<unsigned char> PrivateKey;
extern secp256k1_context* secp256k1_context_sign;


class Key
{
public: 
    /**
     * secp256k1
     */
    static const unsigned int kPrivateKeySize =279;
    
private:
    //! Whether this private key is valid. We check for correctness when modifying the key
    //! data, so fValid should always correspond to the actual state.
    bool fValid_;

    //! The actual byte data
    std::vector<unsigned char> keyData_;

    //! Check whether the 32-byte array pointed to by vch is valid keydata.
    bool static check(const unsigned char* vch);

public: 
    Key(): fValid_(false)
    { 
        keyData_.resize(32); 
        secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
    }
    ~Key(){
        if(secp256k1_context_sign) {
            secp256k1_context_destroy(secp256k1_context_sign);
            secp256k1_context_sign = nullptr;
        }
    }
    //! Simple read-only vector-like interface.
    unsigned int size() const { return (fValid_ ? keyData_.size() : 0); }
    const unsigned char* begin() const { return keyData_.data(); }
    const unsigned char* end() const { return keyData_.data() + size(); }

    template <typename T>
    void set(const T pbegin, const T pend)
    {
        if(size_t(pend-pbegin) == keyData_.size() && check(&pbegin[0]))
        {
            memcpy(keyData_.data(), (unsigned char*)&pbegin[0],keyData_.size());
            fValid_ = true;
        }
        else fValid_ = false;
    }

    friend bool operator==(const Key& a, const Key& b)
    {
        return a.size()==b.size() && memcmp(a.keyData_.data(),b.keyData_.data(),a.size()) == 0;
    }
    // check whether this private key is valid
    bool isValid() const { return fValid_; }

    /**
     * Convert the private key to a PrivateKey (serialized OpenSSL private key data).
     * This is expensive.
     */

    //! Generate a new private key using a cryptographic PRNG.

    void makeNewKey();

    PrivateKey getPrivateKey() const;

    /**
     * Compute the public key from a private key.
     * This is expensive.
     */
    PublicKey getPublicKey() const;

    /**
     * Create a DER-serialized signature.
     * The test_case parameter tweaks the deterministic nonce.
     */
    bool sign(const uint256& hash, std::vector<unsigned char>& vchSig, uint32_t testCase = 0) const;


    /**
     * Verify thoroughly whether a private key and a public key match.
     * This is done using a different mechanism than just regenerating it.
     */
    bool verifyPubKey(const PublicKey& publicKey) const;

    // load private key and check that public key matches
    bool load(const PrivateKey& privateKey, const PublicKey& publicKey);
};


#endif // !PRIVATE_KEY_HPP
